﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Globalization;
using System.Windows.Forms;


namespace FAST
{
    [Serializable()]
    public class FileVersioning
    {
        private List<FileVersioningList> versionList;
        private int overLimitBackupRemoveBit;
        private int restoreBackupRemoveBit;
        private String deletedFilesBackupLocation;
        
        //Method name : FileVersioning() : N.A for constructors.
        //Parameters  : None.
        //Description : An instance of the FileVersioning constructor which stores a string deletedFilesBackupLocation, a List<FileVersioningList> versionList, an int overLimitBackupRemoveBit and an int restoreBackupRemoveBit.
        //Exceptions  : None.
        public FileVersioning()
        {
            deletedFilesBackupLocation = Application.StartupPath + @"\Sessions\Backup\";//location used to store the deleted version files
            versionList = new List<FileVersioningList>(); //A list of list<FileHandler>

            //default set as 0 --> 0 for backup, 1 for remove -> for files over version limit
            overLimitBackupRemoveBit = 0; // backup the oldest file in another location or delete it permanently

            //default set as 0 --> 0 for backup, 1 for remove -> for reverting back versioned files
            restoreBackupRemoveBit = 0; // backup the original file by making a version out of it or delete it permanently

            Serializer ser = new Serializer();
            //check file exist
            if (!File.Exists(Application.StartupPath + @"\Sessions\versionList.dat"))
            {
                versionList = new List<FileVersioningList>();
            }
            else
            {
                versionList = ser.deserializeFVList(Application.StartupPath + @"\Sessions\versionList.dat");
            }

            string message = "";
            int count = 0;
            foreach (FileVersioningList test in versionList)
            {
                message += "index " + count + " ";
            }

        }

        //Method name : getListOfFVList() : List<FileVersioningList>
        //Parameters  : None.
        //Description : Returns the attribute 'versionList' as a List<FileVersioningList> item.
        //Exceptions  : None.
        public List<FileVersioningList> getListOfFVList()
        {
            return this.versionList;
        }
        
        //Method name : setOverLimitBackupRemoveBit() : void
        //Parameters  : bit(type int)
        //Description : Sets the value of overLimitBackupRemoveBit.
        //Exceptions  : OutOfRangeValue Exception: Can only set bit to 0 or 1.
        public void setOverLimitBackupRemoveBit(int bit)
        {
            this.overLimitBackupRemoveBit = bit;
        }
        
        //Method name : getOverLimitBackupRemoveBit() : int
        //Parameters  : None.
        //Description : Returns the value of overLimitBackupRemoveBit.
        //Exceptions  : None.
        public int getOverLimitBackupRemoveBit()
        {
            return this.overLimitBackupRemoveBit;
        }

        //Method name : setRestoreBackupRemoveBit() : void
        //Parameters  : bit(type int)
        //Description : Sets the value of restoreBackupRemoveBit.
        //Exceptions  : OutOfRangeValue Exception: Can only set bit to 0 or 1.
        public void setRestoreBackupRemoveBit(int bit)
        {
            this.restoreBackupRemoveBit = bit;
        }

        //Method name : getRestoreBackupRemoveBit() : int
        //Parameters  : None.
        //Description : Returns the value of restoreBackupRemoveBit.
        //Exceptions  : None.
        public int getRestoreBackupRemoveBit()
        {
            return this.restoreBackupRemoveBit;
        }

        //Method name : revertVersion() : void
        //Parameters  : file(type FileHandler), ver(type int)
        //Description : Reverts a file of version number 'ver' by replacing it with the current 'file'.
        //Exceptions  : InvalidVersions Exception: Invalid value of 'ver' when retrieveing the specified file.
        public void revertVersion(FileHandler file, int ver)
        {
            int idx = file.getVersioningIdx();
            int count = 0;
            if (this.getRestoreBackupRemoveBit() == 0) //backup a version of file before deletion
            {
                this.createVersion(file);
                this.replaceCurrentWithVersion(file, idx, ver, count);
            }
            else if (this.getRestoreBackupRemoveBit() == 1) //delete current file
            {
                this.replaceCurrentWithVersion(file, idx, ver, count);
            }
        }

        //Method name : replaceCurrentWithVersion() : void
        //Parameters  : file(type FileHandler), idx(type int), ver(type int), c(type int)
        //Description : Replaces the current file with its versioned file of version number 'ver'
        //Exceptions  : InvalidVersions Exception: Invalid value of 'ver' when retrieveing the specified file.
        public void replaceCurrentWithVersion(FileHandler file, int idx, int ver, int c)
        {
            File.Delete(file.fullFilePath);
            foreach (FileHandler fh in this.getListOfFVList()[idx].getListOfFH())
            {
                if (ver == this.retrieveVersionNumber(fh.fullFilePath))
                {
                    //move version file to current file location           
                    FileInfo MyFile = new FileInfo(fh.fullFilePath);
                    MyFile.CopyTo(file.fullFilePath, true);
                }
                c++;
            }
        }

        //Method name : createVersion() : void
        //Parameters  : file(type FileHandler)
        //Description : Creates a new version of the current 'file'
        //Exceptions  : FileNotFound Exception: File is invalid or cannot be found.
        public void createVersion(FileHandler file)
        {
           
            //retrieve file versioning index from file
            int idx = file.getVersioningIdx();

            //check for existing versions of target file if earlier versions exist
            if (idx != -1)
            {   //check for version limit, if not yet reach limit, continue
                if (this.getListOfFVList()[idx].getListCount() < this.getListOfFVList()[idx].getVersionLimit())
                {
                    this.getListOfFVList()[idx].createVersionInList(file);
                }
                else //else remove oldest version and add a new one
                {
                    Console.WriteLine("File Version Limit reached. ");
                    String oldestFile = this.getOldestFileToBeRemoved(idx);
                    //Currently removes the versions in list by FCFS order
                    //such that the top of the list will always be removed i.e. list[0]
                    if (this.overLimitBackupRemoveBit == 1) //1 for remove oldest version
                    {
                        Console.WriteLine("Removing oldest file: [" + oldestFile + "]");
                        File.Delete(oldestFile);
                        this.removeCurrentSetNextOldAndCreateNextVer(idx, 0, file);//remove oldest file
                    }
                    else if (this.overLimitBackupRemoveBit == 0)//0 for backup oldest version
                    {
                        String fp = file.getFullFilePath();
                        String drive = fp.Substring(0, fp.LastIndexOf(@":\"));
                        drive = drive + "_drive"; //changes 'C:\' to 'c_drive' folder
                        String pathAfterRoot = fp.Substring(fp.IndexOf(@":\") + 1);
                        pathAfterRoot = pathAfterRoot.Substring(0, pathAfterRoot.LastIndexOf(@"\"));
                        String newParentRootFullFP = this.deletedFilesBackupLocation + drive + @"\" + pathAfterRoot + @"\";
                        if (!Directory.Exists(newParentRootFullFP))
                        {
                            Directory.CreateDirectory(newParentRootFullFP);
                        }
                        String oldestFile2 = newParentRootFullFP + oldestFile.Substring(oldestFile.LastIndexOf(@"\") + 1);
                        File.Move(oldestFile, oldestFile2);
                        this.removeCurrentSetNextOldAndCreateNextVer(idx, 0, file);//remove oldest file
                    }
                }
            }
            //else if no earlier versions exist, create a new list of versions for it
            else
            {
                FileVersioningList fvl = new FileVersioningList();
                this.getListOfFVList().Add(fvl);
                fvl.setFullFP(file.fullFilePath);
                this.getListOfFVList()[this.getListOfFVList().Count - 1].createVersionInList(file);
                file.setVersioningIdx(this.getListOfFVList().Count - 1);
                
            }
           
            //serialize os state
            Serializer ser = new Serializer();
            ser.serializeFVList(Application.StartupPath + @"\Sessions\versionList.dat", versionList);
        }
        
        //Method name : removeCurrentSetNextOldAndCreateNextVer() : void
        //Parameters  : idx(type int), pos(type int), file(type FileHandler)
        //Description : Removes the oldest versioned file, set oldestFileIdx to the next oldest file and create a next version with a new version number.
        //Exceptions  : FileNotFound Exception: File is invalid or cannot be found.
        public void removeCurrentSetNextOldAndCreateNextVer(int idx, int pos, FileHandler file)
        {
            this.setNextOldestVersion(idx);
            this.removeFromListOfFH(idx, pos);
            this.getListOfFVList()[idx].createVersionInList(file);
        }
        
        //Method name : getOldestFileToBeRemoved() : String
        //Parameters  : idx(type int)
        //Description : Returns the full path name of the oldest versioned file to be removed.
        //Exceptions  : FileNotFound Exception: File is invalid or cannot be found.
        public String getOldestFileToBeRemoved(int idx)
        {
            String fullfp = this.getListOfFVList()[idx].getFullFP();
            String filename = fullfp.Substring(0, fullfp.LastIndexOf("."));
            filename = filename.Substring(filename.LastIndexOf("\\") + 1);
            String ext = fullfp.Substring(fullfp.LastIndexOf(".") + 1);

            String drive = fullfp.Substring(0, fullfp.LastIndexOf(@":\"));
            drive = drive + "_drive"; //changes 'C:\' to 'c_drive' folder
            String pathAfterRoot = fullfp.Substring(fullfp.IndexOf(@":\") + 1);
            pathAfterRoot = pathAfterRoot.Substring(0, pathAfterRoot.LastIndexOf(@"\"));
            String newParentRootFullFP = this.getListOfFVList()[idx].getVersionedFilesLocation() + drive + @"\" + pathAfterRoot + @"\";
            if (!Directory.Exists(newParentRootFullFP))
            {
                Directory.CreateDirectory(newParentRootFullFP);
            }
            String fp = newParentRootFullFP + filename + "_v[" + this.getListOfFVList()[idx].getOldestVersion() + "]." + ext;

            return fp;
        }

        //Method name : setNextOldestVersion() : void
        //Parameters  : idx(type int)
        //Description : Sets the next file to be the oldest version after removing the current oldest version.
        //Exceptions  : FileNotFound Exception: File is invalid or cannot be found.
        public void setNextOldestVersion(int idx)
        {
            this.getListOfFVList()[idx].setOldestVersion(this.getListOfFVList()[idx].getOldestVersion() + 1);
        }

        //Method name : getListCount() : int
        //Parameters  : None.
        //Description : Returns the number of items in the list.
        //Exceptions  : None.
        public int getListCount()
        {//returns the number of FileVersioningList objects in list
            return this.getListOfFVList().Count;
        }

        //Method name : retrieveVersionNumber() : int
        //Parameters  : fp(type String)
        //Description : Returns the version number of the current file given its full file path.
        //Exceptions  : FileNotFound Exception: File is invalid or cannot be found.
        public int retrieveVersionNumber(String fp)
        {//returns the file version value
            String temp = fp.Substring(0, fp.LastIndexOf("]"));
            temp = temp.Substring(fp.LastIndexOf("[") + 1);
            return System.Convert.ToInt32(temp);
        }

        //Method name : removeFromListOfFH() : void
        //Parameters  : idx(type int), positionInList(type int)
        //Description : Removes current file of index 'positionInList' and 'idx' in List<FileVersioningList>
        //Exceptions  : ArrayOutOfBounds Exception: Invalid value of range given.
        public void removeFromListOfFH(int idx, int positionInList)
        {//removes FileHandler object from FileVersioningList of index 'idx' and at position 'positionInList' that list
            this.getListOfFVList()[idx].getListOfFH().Remove(this.getListOfFVList()[idx].getListOfFH()[positionInList]);
        }

        //Method name : setVersionListByIdx() : void
        //Parameters  : vList(type List<FileHandler>), idx(type int)
        //Description : For UI to store comments of a particular file
        //Exceptions  : None.
        public void setVersionListByIdx(List<FileHandler> vList, int idx)
        {
            if (idx != -1)
            {
                this.versionList[idx].setVersionList(vList);
                Serializer ser = new Serializer();
                ser.serializeFVList(Application.StartupPath + @"\Sessions\versionList.dat", versionList);
            }

        }
        
    }
}
